/*-------------------------------------------------------------------------
 *
 *	fe-trace.c
 *	  用于 libpq 协议跟踪的函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/interfaces/libpq/fe-trace.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include <ctype.h>
#include <limits.h>
#include <sys/time.h>
#include <time.h>

#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#endif

#include "libpq-fe.h"
#include "libpq-int.h"
#include "port/pg_bswap.h"


/* 启用跟踪 */
void PQtrace(PGconn *fc_conn, FILE *fc_debug_port)
{
	if (fc_conn == NULL)
		return;
	PQuntrace(fc_conn);
	if (fc_debug_port == NULL)
		return;

	fc_conn->Pfdebug = fc_debug_port;
	fc_conn->traceFlags = 0;
}

/* 禁用跟踪 */
void PQuntrace(PGconn *fc_conn)
{
	if (fc_conn == NULL)
		return;
	if (fc_conn->Pfdebug)
	{
		fflush(fc_conn->Pfdebug);
		fc_conn->Pfdebug = NULL;
	}

	fc_conn->traceFlags = 0;
}

/* 为当前跟踪会话设置标志 */
void PQsetTraceFlags(PGconn *fc_conn, int fc_flags)
{
	if (fc_conn == NULL)
		return;
	/* 如果 PQtrace() 失败，则不执行任何操作。 */
	if (fc_conn->Pfdebug == NULL)
		return;
	fc_conn->traceFlags = fc_flags;
}

/*
 * 将当前时间（带微秒）打印到调用者提供的
 * 缓冲区。
 * 摘自 setup_formatted_log_time，但简单得多。
 */
static void fc_pqTraceFormatTimestamp(char *fc_timestr, size_t fc_ts_len)
{
	struct timeval fc_tval;
	time_t		fc_now;

	gettimeofday(&fc_tval, NULL);

	/*
	 * MSVC 的 timeval 实现使用 long 类型作为 tv_sec，然而，
	 * localtime() 期望一个 time_t 指针。在这里，我们将 tv_sec 赋值给
	 * 一个本地 time_t 变量，以便将正确的指针类型传递给 localtime()。
	 */
	fc_now = fc_tval.tv_sec;
	strftime(fc_timestr, fc_ts_len,
			 "%Y-%m-%d %H:%M:%S",
			 localtime(&fc_now));
	/* 附加微秒 */
	snprintf(fc_timestr + strlen(fc_timestr), fc_ts_len - strlen(fc_timestr),
			 ".%06u", (unsigned int) (fc_tval.tv_usec));
}

/*
 *   pqTraceOutputByte1：将 1 字符消息输出到日志
 */
static void fc_pqTraceOutputByte1(FILE *fc_pfdebug, const char *fc_data, int *fc_cursor)
{
	const char *fc_v = fc_data + *fc_cursor;

	/*
	 * 以十六进制格式显示不可打印的数据，包括结束的 \0
	 * 用于完成 ErrorResponse 和 NoticeResponse 消息。
	 */
	if (!isprint((unsigned char) *fc_v))
		fprintf(fc_pfdebug, " \\x%02x", *fc_v);
	else
		fprintf(fc_pfdebug, " %c", *fc_v);
	*fc_cursor += 1;
}

/*
 *   pqTraceOutputInt16：将 2 字节整数消息输出到日志
 */
static int fc_pqTraceOutputInt16(FILE *fc_pfdebug, const char *fc_data, int *fc_cursor)
{
	uint16		fc_tmp;
	int			fc_result;

	memcpy(&fc_tmp, fc_data + *fc_cursor, 2);
	*fc_cursor += 2;
	fc_result = (int) pg_ntoh16(fc_tmp);
	fprintf(fc_pfdebug, " %d", fc_result);

	return fc_result;
}

/*
 *   pqTraceOutputInt32：将 4 字节整数消息输出到日志
 *
 * 如果 'suppress' 为真，则打印字面值 NNNN，而不是实际数字。
 */
static int fc_pqTraceOutputInt32(FILE *fc_pfdebug, const char *fc_data, int *fc_cursor, bool fc_suppress)
{
	int			fc_result;

	memcpy(&fc_result, fc_data + *fc_cursor, 4);
	*fc_cursor += 4;
	fc_result = (int) pg_ntoh32(fc_result);
	if (fc_suppress)
		fprintf(fc_pfdebug, " NNNN");
	else
		fprintf(fc_pfdebug, " %d", fc_result);

	return fc_result;
}

/*
 *   pqTraceOutputString：将字符串消息输出到日志
 */
static void fc_pqTraceOutputString(FILE *fc_pfdebug, const char *fc_data, int *fc_cursor, bool fc_suppress)
{
	int			fc_len;

	if (fc_suppress)
	{
		fprintf(fc_pfdebug, " \"SSSS\"");
		*fc_cursor += strlen(fc_data + *fc_cursor) + 1;
	}
	else
	{
		fc_len = fprintf(fc_pfdebug, " \"%s\"", fc_data + *fc_cursor);

		/*
		 * 这是一个以 null 终止的字符串。因此在从 len 减去 3
		 * （即双引号和空格的长度）后加 1。
		 */
		*fc_cursor += (fc_len - 3 + 1);
	}
}

/*
 * pqTraceOutputNchar：将恰好 len 字节的字符串消息输出到日志
 */
static void fc_pqTraceOutputNchar(FILE *fc_pfdebug, int fc_len, const char *fc_data, int *fc_cursor)
{
	int			fc_i,
				fc_next;			/* 首个字符尚未打印 */
	const char *fc_v = fc_data + *fc_cursor;

	fprintf(fc_pfdebug, " \'");

	for (fc_next = fc_i = 0; fc_i < fc_len; ++fc_i)
	{
		if (isprint((unsigned char) fc_v[fc_i]))
			continue;
		else
		{
			fwrite(fc_v + fc_next, 1, fc_i - fc_next, fc_pfdebug);
			fprintf(fc_pfdebug, "\\x%02x", fc_v[fc_i]);
			fc_next = fc_i + 1;
		}
	}
	if (fc_next < fc_len)
		fwrite(fc_v + fc_next, 1, fc_len - fc_next, fc_pfdebug);

	fprintf(fc_pfdebug, "\'");
	*fc_cursor += fc_len;
}

/*
 * 按协议消息类型输出函数
 */

/* NotificationResponse */
static void fc_pqTraceOutputA(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	fprintf(fc_f, "NotificationResponse\t");
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
}

/* Bind */
static void fc_pqTraceOutputB(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	int			fc_nparams;

	fprintf(fc_f, "Bind\t");
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_nparams = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nparams; fc_i++)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	fc_nparams = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nparams; fc_i++)
	{
		int			fc_nbytes;

		fc_nbytes = fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
		if (fc_nbytes == -1)
			continue;
		fc_pqTraceOutputNchar(fc_f, fc_nbytes, fc_message, fc_cursor);
	}

	fc_nparams = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
	for (int fc_i = 0; fc_i < fc_nparams; fc_i++)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
}

/* Close(F) 或 CommandComplete(B) */
static void fc_pqTraceOutputC(FILE *fc_f, bool fc_toServer, const char *fc_message, int *fc_cursor)
{
	if (fc_toServer)
	{
		fprintf(fc_f, "Close\t");
		fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	}
	else
	{
		fprintf(fc_f, "CommandComplete\t");
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	}
}

/* Describe(F) 或 DataRow(B) */
static void fc_pqTraceOutputD(FILE *fc_f, bool fc_toServer, const char *fc_message, int *fc_cursor)
{
	if (fc_toServer)
	{
		fprintf(fc_f, "Describe\t");
		fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	}
	else
	{
		int			fc_nfields;
		int			fc_len;
		int			fc_i;

		fprintf(fc_f, "DataRow\t");
		fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
		for (fc_i = 0; fc_i < fc_nfields; fc_i++)
		{
			fc_len = fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
			if (fc_len == -1)
				continue;
			fc_pqTraceOutputNchar(fc_f, fc_len, fc_message, fc_cursor);
		}
	}
}

/* NoticeResponse / ErrorResponse */
static void fc_pqTraceOutputNR(FILE *fc_f, const char *fc_type, const char *fc_message, int *fc_cursor,
				bool fc_regress)
{
	fprintf(fc_f, "%s\t", fc_type);
	for (;;)
	{
		char		fc_field;
		bool		fc_suppress;

		fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
		fc_field = fc_message[*fc_cursor - 1];
		if (fc_field == '\0')
			break;

		fc_suppress = fc_regress && (fc_field == 'L' || fc_field == 'F' || fc_field == 'R');
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, fc_suppress);
	}
}

/* Execute(F) 或 ErrorResponse(B) */
static void fc_pqTraceOutputE(FILE *fc_f, bool fc_toServer, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	if (fc_toServer)
	{
		fprintf(fc_f, "Execute\t");
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
	}
	else
		fc_pqTraceOutputNR(fc_f, "ErrorResponse", fc_message, fc_cursor, fc_regress);
}

/* CopyFail */
static void fc_pqTraceOutputf(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "CopyFail\t");
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
}

/* FunctionCall */
static void fc_pqTraceOutputF(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	int			fc_nfields;
	int			fc_nbytes;

	fprintf(fc_f, "FunctionCall\t");
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
	{
		fc_nbytes = fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
		if (fc_nbytes == -1)
			continue;
		fc_pqTraceOutputNchar(fc_f, fc_nbytes, fc_message, fc_cursor);
	}

	fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
}

/* CopyInResponse */
static void fc_pqTraceOutputG(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	int			fc_nfields;

	fprintf(fc_f, "CopyInResponse\t");
	fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
}

/* CopyOutResponse */
static void fc_pqTraceOutputH(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	int			fc_nfields;

	fprintf(fc_f, "CopyOutResponse\t");
	fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
}

/* BackendKeyData */
static void fc_pqTraceOutputK(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	fprintf(fc_f, "BackendKeyData\t");
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
}

/* Parse */
static void fc_pqTraceOutputP(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	int			fc_nparams;

	fprintf(fc_f, "Parse\t");
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_nparams = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nparams; fc_i++)
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
}

/* Query */
static void fc_pqTraceOutputQ(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "Query\t");
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
}

/* Authentication */
static void fc_pqTraceOutputR(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "Authentication\t");
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
}

/* ParameterStatus */
static void fc_pqTraceOutputS(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "ParameterStatus\t");
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
	fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
}

/* ParameterDescription */
static void fc_pqTraceOutputt(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	int			fc_nfields;

	fprintf(fc_f, "ParameterDescription\t");
	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
}

/* RowDescription */
static void fc_pqTraceOutputT(FILE *fc_f, const char *fc_message, int *fc_cursor, bool fc_regress)
{
	int			fc_nfields;

	fprintf(fc_f, "RowDescription\t");
	fc_nfields = fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);

	for (int fc_i = 0; fc_i < fc_nfields; fc_i++)
	{
		fc_pqTraceOutputString(fc_f, fc_message, fc_cursor, false);
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, fc_regress);
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
		fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
	}
}

/* NegotiateProtocolVersion */
static void fc_pqTraceOutputv(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "NegotiateProtocolVersion\t");
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
	fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
}

/* FunctionCallResponse */
static void fc_pqTraceOutputV(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	int			fc_len;

	fprintf(fc_f, "FunctionCallResponse\t");
	fc_len = fc_pqTraceOutputInt32(fc_f, fc_message, fc_cursor, false);
	if (fc_len != -1)
		fc_pqTraceOutputNchar(fc_f, fc_len, fc_message, fc_cursor);
}

/* CopyBothResponse */
static void fc_pqTraceOutputW(FILE *fc_f, const char *fc_message, int *fc_cursor, int fc_length)
{
	fprintf(fc_f, "CopyBothResponse\t");
	fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);

	while (fc_length > *fc_cursor)
		fc_pqTraceOutputInt16(fc_f, fc_message, fc_cursor);
}

/* ReadyForQuery */
static void fc_pqTraceOutputZ(FILE *fc_f, const char *fc_message, int *fc_cursor)
{
	fprintf(fc_f, "ReadyForQuery\t");
	fc_pqTraceOutputByte1(fc_f, fc_message, fc_cursor);
}

/*
 * 将给定消息打印到跟踪输出流。
 */
void pqTraceOutputMessage(PGconn *fc_conn, const char *fc_message, bool fc_toServer)
{
	char		fc_id;
	int			fc_length;
	char	   *fc_prefix = fc_toServer ? "F" : "B";
	int			fc_logCursor = 0;
	bool		fc_regress;

	if ((fc_conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
	{
		char		fc_timestr[128];

		fc_pqTraceFormatTimestamp(fc_timestr, sizeof(fc_timestr));
		fprintf(fc_conn->Pfdebug, "%s\t", fc_timestr);
	}
	fc_regress = (fc_conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;

	fc_id = fc_message[fc_logCursor++];

	memcpy(&fc_length, fc_message + fc_logCursor, 4);
	fc_length = (int) pg_ntoh32(fc_length);
	fc_logCursor += 4;

	/*
	 * 在回归模式下，抑制 ErrorResponse 和
	 * NoticeResponse 的长度。F（文件名），L（行号）和 R（例程
	 * 名称）字段在服务器代码修改时可能会更改，如果它们的
	 * 长度与原始值不同，则会导致测试失败。
	 */
	if (fc_regress && !fc_toServer && (fc_id == 'E' || fc_id == 'N'))
		fprintf(fc_conn->Pfdebug, "%s\tNN\t", fc_prefix);
	else
		fprintf(fc_conn->Pfdebug, "%s\t%d\t", fc_prefix, fc_length);

	switch (fc_id)
	{
		case '1':
			fprintf(fc_conn->Pfdebug, "ParseComplete");
			/* 无消息内容 */
			break;
		case '2':
			fprintf(fc_conn->Pfdebug, "BindComplete");
			/* 无消息内容 */
			break;
		case '3':
			fprintf(fc_conn->Pfdebug, "CloseComplete");
			/* 无消息内容 */
			break;
		case 'A':				/* Notification Response */
			fc_pqTraceOutputA(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'B':				/* Bind */
			fc_pqTraceOutputB(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'c':
			fprintf(fc_conn->Pfdebug, "CopyDone");
			/* 无消息内容 */
			break;
		case 'C':				/* Close(F) 或 Command Complete(B) */
			fc_pqTraceOutputC(fc_conn->Pfdebug, fc_toServer, fc_message, &fc_logCursor);
			break;
		case 'd':				/* 复制数据 */
			/* 删除 COPY 数据以减少日志记录的开销。 */
			break;
		case 'D':				/* Describe(F) 或 Data Row(B) */
			fc_pqTraceOutputD(fc_conn->Pfdebug, fc_toServer, fc_message, &fc_logCursor);
			break;
		case 'E':				/* Execute(F) 或 Error Response(B) */
			fc_pqTraceOutputE(fc_conn->Pfdebug, fc_toServer, fc_message, &fc_logCursor,
						   fc_regress);
			break;
		case 'f':				/* Copy Fail */
			fc_pqTraceOutputf(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'F':				/* Function Call */
			fc_pqTraceOutputF(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'G':				/* 开始复制输入 */
			fc_pqTraceOutputG(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'H':				/* Flush(F) 或 Start Copy Out(B) */
			if (!fc_toServer)
				fc_pqTraceOutputH(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			else
				fprintf(fc_conn->Pfdebug, "Flush");	/* 无消息内容 */
			break;
		case 'I':
			fprintf(fc_conn->Pfdebug, "EmptyQueryResponse");
			/* 无消息内容 */
			break;
		case 'K':				/* 来自后端的密钥数据 */
			fc_pqTraceOutputK(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'n':
			fprintf(fc_conn->Pfdebug, "NoData");
			/* 无消息内容 */
			break;
		case 'N':
			fc_pqTraceOutputNR(fc_conn->Pfdebug, "NoticeResponse", fc_message,
							&fc_logCursor, fc_regress);
			break;
		case 'P':				/* Parse */
			fc_pqTraceOutputP(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'Q':				/* Query */
			fc_pqTraceOutputQ(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'R':				/* Authentication */
			fc_pqTraceOutputR(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 's':
			fprintf(fc_conn->Pfdebug, "PortalSuspended");
			/* 无消息内容 */
			break;
		case 'S':				/* 参数状态(B)或同步(F) */
			if (!fc_toServer)
				fc_pqTraceOutputS(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			else
				fprintf(fc_conn->Pfdebug, "Sync"); /* 无消息内容 */
			break;
		case 't':				/* 参数描述 */
			fc_pqTraceOutputt(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'T':				/* 行描述 */
			fc_pqTraceOutputT(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_regress);
			break;
		case 'v':				/* 协商协议版本 */
			fc_pqTraceOutputv(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'V':				/* 函数调用响应 */
			fc_pqTraceOutputV(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		case 'W':				/* 开始双向复制 */
			fc_pqTraceOutputW(fc_conn->Pfdebug, fc_message, &fc_logCursor, fc_length);
			break;
		case 'X':
			fprintf(fc_conn->Pfdebug, "Terminate");
			/* 无消息内容 */
			break;
		case 'Z':				/* 准备查询 */
			fc_pqTraceOutputZ(fc_conn->Pfdebug, fc_message, &fc_logCursor);
			break;
		default:
			fprintf(fc_conn->Pfdebug, "Unknown message: %02x", fc_id);
			break;
	}

	fputc('\n', fc_conn->Pfdebug);

	/*
	 * 验证打印例程是否正确。注意，单字节
	 * 消息标识符不包括在长度中，但我们的光标
	 * 确实包含它。
	 */
	if (fc_logCursor - 1 != fc_length)
		fprintf(fc_conn->Pfdebug,
				"mismatched message length: consumed %d, expected %d\n",
				fc_logCursor - 1, fc_length);
}

/*
 * 将特殊消息（不包含类型字节的消息）打印到跟踪输出
 * 流中。
 */
void pqTraceOutputNoTypeByteMessage(PGconn *fc_conn, const char *fc_message)
{
	int			fc_length;
	int			fc_logCursor = 0;

	if ((fc_conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
	{
		char		fc_timestr[128];

		fc_pqTraceFormatTimestamp(fc_timestr, sizeof(fc_timestr));
		fprintf(fc_conn->Pfdebug, "%s\t", fc_timestr);
	}

	memcpy(&fc_length, fc_message + fc_logCursor, 4);
	fc_length = (int) pg_ntoh32(fc_length);
	fc_logCursor += 4;

	fprintf(fc_conn->Pfdebug, "F\t%d\t", fc_length);

	switch (fc_length)
	{
		case 16:				/* 取消请求 */
			fprintf(fc_conn->Pfdebug, "CancelRequest\t");
			fc_pqTraceOutputInt32(fc_conn->Pfdebug, fc_message, &fc_logCursor, false);
			fc_pqTraceOutputInt32(fc_conn->Pfdebug, fc_message, &fc_logCursor, false);
			fc_pqTraceOutputInt32(fc_conn->Pfdebug, fc_message, &fc_logCursor, false);
			break;
		case 8:					/* GSSENC请求或SSL请求 */
			/* 这些消息不会到达这里。 */
		default:
			fprintf(fc_conn->Pfdebug, "Unknown message: length is %d", fc_length);
			break;
	}

	fputc('\n', fc_conn->Pfdebug);
}
